package testtiles.object;

import java.awt.Graphics2D;

import testtiles.state.UnitFollowLeaderState;
import testtiles.state.UnitHoldState;
import testtiles.state.UnitMoveState;
import testtiles.state.UnitStayState;
import butines.core.math2d.Vector2D;
import butines.game.AbstractGameObject;
import butines.game.GameObjectMoving;
import butines.game.Message;
import butines.game.Messenger;
import butines.game.Sprite;
import butines.game.Sprite.Animation;
import butines.game.State;
import butines.game.StateMachine;

public class Unit extends GameObjectMoving {

	private StateMachine<Unit> stateMachine = new StateMachine<Unit>(this);
	private Status status = new Status();

	private Unit leader;
	private Vector2D leaderOffset;
	
	private Vector2D targetPoint = new Vector2D();

	private Messenger messenger;
	private Sprite sprite;
	private Animations animations;
	
	private UnitGroup group;
	private Unit enemy;
	private Weapon weapon;

	public Unit() {
		doStay();
	}

	public void draw(Graphics2D g2d) {
		if (sprite == null) {
			super.draw(g2d);
		} else {
			sprite.draw(g2d, getPosition().x, getPosition().y, getHeading().theta());
		}
	}
	
	private void updatePosition(float tpf) {
		getNeighbors().clear();
		
		if (getSteering().getSeparation().isTurned() || getSteering().getCohesion().isTurned() ) {
			findNeighbors(getPosition(), getViewDistance());
		}
		
		if (getSteering().getAvoidance().isTurned()) {
			findNeighbors(getPosition(), getRadius() * 4f);
		}
		
		Vector2D steeringForce = getSteering().calculate();

		applySteeringForce(steeringForce);

		if (stateMachine.inState(UnitStayState.class)) {
			applyBrakingForce(.7f);
		}

	}

	public void update(float tpf) {
		sprite.update(tpf);

		updatePosition(tpf);

		stateMachine.update();
	}

	@Override
	protected void onCollide(AbstractGameObject obstacle) {
		if (messenger != null) {
			messenger.dispatch(Messages.COLLIDED_WITH, obstacle, this, 0);
		}
	}

	public boolean handleMessage(Message message) {
		final Messages type = message.getType();

		switch (type) {
			case STAY: {
				doStay();
				return true;
			}
			case HOLD: {
				doHold();
				return true;
			}
			case FOLLOW_LEADER: {
				leader = message.getSender();
				leaderOffset = message.getArgs(0);
				changeState(UnitFollowLeaderState.getInstance());
				return true;
			}
			case MOVE: {
				Number x = message.getArgs(0);
				Number y = message.getArgs(1);
				targetPoint.set(x.intValue(), y.intValue());
				doMove();
				return true;
			}
			case RECEIVED_DAMAGE: {
				int damage = message.getArgs(0);
				status.health -= damage;
				if (status.health <= 0) {
					doDie();
				}
				return true;
			}
			case ATTACK: {
				Unit enemy = message.getArgs(0);
				if (enemy != null && enemy != this && enemy.isAlive()) {
					this.enemy = enemy;
					doAttack();
				}
				return true;
			}
		}

		return stateMachine.handlerMessage(message);
	}

	public void changeState(State<Unit> newState) {
		stateMachine.changeState(newState);
	}

	@Override
	public void release() {
		super.release();
		setGroup(null);
	}

	public void doStay() {
		changeState(UnitStayState.getInstance());
	}

	public void doHold() {
		changeState(UnitHoldState.getInstance());
	}
	
	public void doMove() {
		changeState(UnitMoveState.getInstance());
	}

	public void doDie() {
		// TODO
	}
	
	public void doAttack() {
		// TODO
	}
	
	public StateMachine<Unit> getStateMachine() {
		return stateMachine;
	}

	public Status getStatus() {
		return status;
	}
	
	public Animations getAnimations() {
		return animations;
	}

	public Sprite getSprite() {
		return sprite;
	}

	public void setSprite(Sprite sprite) {
		this.sprite = sprite;
		this.animations = (sprite == null) ? null : new Animations(sprite);
	}
	
	public Vector2D getTargetPoint() {
		return targetPoint;
	}

	public void setTargetPoint(Vector2D targetPoint) {
		this.targetPoint.set(targetPoint);
	}

	public Weapon getWeapon() {
		return weapon;
	}

	public void setWeapon(Weapon weapon) {
		this.weapon = weapon;
	}

	public Unit getEnemy() {
		return enemy;
	}

	public Unit getLeader() {
		return leader;
	}

	public Vector2D getLeaderOffset() {
		return leaderOffset;
	}

	public void setLeaderOffset(Vector2D leaderOffset) {
		this.leaderOffset = leaderOffset;
	}

	public void setLeader(Unit leader) {
		this.leader = leader;
	}

	public Messenger getMessenger() {
		return messenger;
	}

	public void setMessenger(Messenger messenger) {
		this.messenger = messenger;
	}

	public UnitGroup getGroup() {
		return group;
	}

	public void setGroup(UnitGroup group) {
		if (this.group != null) {
			this.group.remove(this);
		}
		this.group = group;
		if (this.group != null) {
			this.group.add(this);
		}
	}

	public class Animations {

		private final Animation stay;
		private final Animation walk;
		private final Animation attack;

		public Animations(Sprite sprite) {
			stay = sprite.newAnim("stay");
			walk = sprite.newAnim("walk");
			attack = sprite.newAnim("attack");
		}

		public Animation getStay() {
			return stay;
		}

		public Animation getWalk() {
			return walk;
		}

		public Animation getAttack() {
			return attack;
		}

	}

	public class Status {
		
		private int health = 100;
		private int maxHealth = 100;

		public int getHealth() {
			return health;
		}

		public void setHealth(int health) {
			this.health = health;
		}

		public int getMaxHealth() {
			return maxHealth;
		}

		public void setMaxHealth(int maxHealth) {
			this.maxHealth = maxHealth;
		}
		
	}

}
